namespace EFML_Runtime.Internal
{
    using System;
    using System.Collections.Generic;
    using System.Drawing;
    using System.IO;
    using System.Text;
    using System.Windows.Forms;

    using Creek.UI.EFML;
    using Creek.UI.EFML.Base;
    using Creek.UI.EFML.Base.CSS;
    using Creek.UI.EFML.Base.Controls;
    using Creek.UI.EFML.Base.Controls.Navigator;
    using Creek.UI.EFML.Base.EFML.Elements;
    using Creek.UI.EFML.Base.Exceptions;
    using Creek.UI.EFML.Base.JS;
    using Creek.UI.EFML.Base.JS.ScriptingTypes;
    using Creek.UI.EFML.Base.JS.ScriptingTypes.Console;
    using Creek.UI.EFML.UI_Elements;
    using Creek.UI.Effects;

    using EFML_Runtime.Internal.Base.JS.ScriptingTypes.Transitions;

    using Microsoft.ClearScript;
    using Microsoft.ClearScript.Windows;
    using Microsoft.JScript;
    using Microsoft.VisualBasic;

    using Button = Creek.UI.EFML.UI_Elements.Button;
    using Document = Creek.UI.EFML.Document;
    using Dropdown = Creek.UI.EFML.UI_Elements.Dropdown;
    using Extensions = Creek.UI.EFML.Extensions;
    using Label = Creek.UI.EFML.UI_Elements.Label;
    using Line = Creek.UI.EFML.UI_Elements.Line;
    using Link = Creek.UI.EFML.UI_Elements.Link;
    using TextBox = Creek.UI.EFML.UI_Elements.TextBox;

    public class EfmlForm
    {
        #region Static Fields

        private static Document doc;

        private static WindowsScriptEngine engine;

        private static ControlProvider provider = new ControlProvider();

        #endregion

        #region Delegates

        public delegate IUIElement DollarSignFunc(object arg1, object arg2 = null);

        #endregion

        // [DebuggerStepThrough]

        #region Public Methods and Operators

        public static Form Build(byte[] by, JScriptEngine eng, string strings)
        {
            var b = new Builder(strings);
            b.Load(by);

            engine = eng;

            return BuildForm(b.document);
        }

        public static Form Build(string s, JScriptEngine eng, string strings)
        {
            return Build(Encoding.ASCII.GetBytes(s), eng, strings);
        }

        public static Form Build(Stream s, JScriptEngine eng, string strings)
        {
            return Build(new StreamReader(s).ReadToEnd(), eng, strings);
        }

        #endregion

        #region Methods

        private static void BuildControls(Control parent, IEnumerable<ElementBase> d)
        {
            foreach (UiElement b in d)
            {
                Control c = null;
                if (b is Label)
                {
                    var bc = b as Label;
                    c = provider[Tag.Label];
                }
                if (b is Button)
                {
                    var bc = b as Button;
                    var cc = provider[Tag.Button] as System.Windows.Forms.Button;
                    c = cc;
                }
                if (b is TextBox)
                {
                    var bc = b as TextBox;
                    c = provider[Tag.Textbox];
                    var cc = c as PlaceholderTextBox;
                    cc.TextChanged += (sender, args) => bc.value = cc.Text;
                    cc.PlaceholderText = bc.placeholder;
                }
                if (b is WebView)
                {
                    var bc = b as WebView;
                    /*c = provider[Tag.WebView];
                    var cc = c as WebBrowser;
                    cc.TextChanged += (sender, args) => bc.value = cc.Text;*/
                }
                if (b is Checkbox)
                {
                    var bc = b as Checkbox;
                    var bcc = provider[Tag.Checkbox] as CheckBox;
                    bcc.Checked = bc.Value;
                    c = bcc;
                }
                if (b is Div)
                {
                    var bc = b as Div;
                    var bcc = provider[Tag.Div] as DivPanel;
                    bcc.BorderStyle = bc.border;

                    BuildControls(bcc, bc.Childs);

                    c = bcc;
                }
                if (b is Break)
                {
                    var bc = b as Break;
                    var bcc = provider[Tag.Break] as Panel;
                    bcc.BorderStyle = bc.border;

                    bcc.Size = new Size(parent.Size.Width, 15);

                    c = bcc;
                }
                if (b is Radio)
                {
                    var bc = b as Radio;
                    var bcc = provider[Tag.Radiobutton] as RadioButton;
                    bcc.Checked = bc.Value;
                    c = bcc;
                }
                if (b is FlashElement)
                {
                    //c = (Control) Activator.CreateInstance((b as FlashElement).Type);
                }
                if (b is Dropdown)
                {
                    var bc = b as Dropdown;
                    var bcc = provider[Tag.Dropdown] as ComboBox;
                    bcc.Items.AddRange(bc.Childs.ToArray());
                    bcc.DropDownStyle = bc.style == DropdownStyle.list
                                            ? ComboBoxStyle.DropDownList
                                            : ComboBoxStyle.DropDown;
                    bcc.SelectedIndexChanged += (sender, args) => engine.Execute(bc.Events["onselectionchange"]);

                    c = bcc;
                }
                if (b is Navigator)
                {
                    var bc = b as Navigator;
                    var bcc = provider[Tag.Navigator] as PageNavigator;

                    if (bc.Pages.Count <= 1)
                    {
                        throw new EfmlException("Navigator '" + bc.ID + "' required more than one page");
                    }

                    if (bc.backgroundimage != null)
                    {
                        bcc.BackGround = bc.backgroundimage;
                    }
                    if (bc.backbuttonimage != null)
                    {
                        bcc.BackwardButton = bc.backbuttonimage;
                    }
                    if (bc.forwardbuttonimage != null)
                    {
                        bcc.ForwardButton = bc.forwardbuttonimage;
                    }

                    foreach (var p in bc.Pages)
                    {
                        var pp = new UserControl { Text = p.Caption, Name = p.Caption };

                        BuildControls(pp, p.Childs);
                        bcc.Pages.Add(pp);
                    }
                    bcc.RefreshPages();

                    c = bcc;
                }
                if (b is Link)
                {
                    var bc = b as Link;
                    var cc = provider[Tag.Link] as LinkLabel;
                    c = cc;
                }
                if (b is Line)
                {
                    var bc = b as Line;
                    var cc = provider[Tag.Line] as Creek.UI.EFML.Base.Controls.Line;
                    cc.BorderColor = bc.bordercolor;
                    c = cc;
                }

                if (c != null)
                {
                    c.Text = b.value;
                    c.Name = b.ID;
                    c.Click += (sender, args) => engine.Execute(b.Events["onclick"]);
                    c.MouseEnter += (sender, args) => engine.Execute(b.Events["onhover"]);
                    c.MouseLeave += (sender, args) => engine.Execute(b.Events["onleave"]);

                    c.Tag = b.ToString();

                    parent.Controls.Add(c);
                }
            }
        }

        private static Form BuildForm(Document d)
        {
            var layout = new FlowLayoutPanel { Dock = DockStyle.Fill };

            doc = d;

            foreach (MetaElement e in d.Header.Meta)
            {
                switch (e.Name)
                {
                    case "ControlProvider":
                        provider.Load(new FileStream(e.Content, FileMode.OpenOrCreate));
                        break;
                }
            }

            if (provider.Controls.Count == 0)
            {
                provider = new WinformsControlProvider();
            }

            var f = Activator.CreateInstance(provider.Controls[Tag.Window]) as Form;

            foreach (MetaElement e in d.Header.Meta)
            {
                switch (e.Name)
                {
                    case "Title":
                        f.Text = e.Content;
                        break;
                }
            }

            foreach (var scriptingType in provider.ScriptingTypes)
            {
                engine.Add(scriptingType.Name, scriptingType);
            }
            foreach (var func in provider.ScriptingFuncs)
            {
                engine.Execute(func);
            }

            BuildControls(layout, d.Body);

            StyleChanger.Execute(d, layout.Controls);

            InitJs(f, layout);
            try
            {
                ExecuteScripts(d);
            }
            catch (Exception ex)
            {
                throw new JsException(ex.Message);
            }

            f.Controls.Add(layout);

            return f;
        }

        private static void ExecuteScripts(Document d)
        {
            foreach (ScriptElement s in d.Header.Scripts)
            {
                engine.Execute(s.Source);
            }
        }

        private static void InitJs(Form form, FlowLayoutPanel layout)
        {
            engine.Add("parent", form);
            engine.Add("window", new Window(form));
            engine.Add(
                "$",
                new DollarSignFunc(
                    (arg1, arg2) =>
                        {
                            var name = arg1 as string;
                            if (name == null)
                            {
                                new Window(form).AddEventHandler("Load", arg1);
                            }
                            else if (arg2 != null)
                            {
                                new Window(form).AddEventHandler(name, arg2);
                            }
                            else
                            {
                                return
                                    new Creek.UI.EFML.Base.JS.ScriptingTypes.Document(doc, layout).GetElementById(name);
                            }
                            return new PlaceholderTextBox();
                        }));

            engine.Add("prompt", new Func<string, string>(s => Interaction.InputBox(s)));
            engine.Add("createObj", new Func<string, object>(Functions.CreateObj));
            engine.Add("require", new Action<string>(s => Functions.Require(engine, s)));
            engine.Add("import", new Func<string, string, object[], object>(Functions.Import));

            engine.Add("host", new ExtendedHostFunctions());
            engine.Add("clr", new HostTypeCollection("System", "System.Core", "System.Drawing", "System.Windows.Forms"));

            engine.Add("XMLHttpRequest", typeof(XmlHttpRequest));
            engine.Add("Extensions", typeof(Extensions));
            engine.Add("JSON", typeof(JSON));
            engine.Add("Object", typeof(Creek.UI.EFML.Base.JS.ScriptingTypes.Object));
            engine.Add("Math", new Creek.UI.EFML.Base.JS.ScriptingTypes.Math());
            engine.Add("screen", new Creek.UI.EFML.Base.JS.ScriptingTypes.Screen());
            engine.Add("console", new FirebugConsole());

            engine.Add("escape", new Func<string, string>(GlobalObject.escape));
            engine.Add("unescape", new Func<string, string>(GlobalObject.unescape));

            engine.Add("parseFloat", new Func<string, float>(float.Parse));
            engine.Add("parseInt", new Func<string, int>(int.Parse));
            engine.Add("String", new Func<string, string>(s => s.toString()));

            engine.Add("ui", new Func<object, Control>(o => (Control)o));
            engine.Add(
                "color", new Func<string, Color>(s => new Creek.UI.EFML.Base.CSS.Converters.ColorConverter().Convert(s)));

            engine.Add("eval", new Func<string, object>(o => engine.Evaluate(o)));

            engine.Add("Transition", typeof(Transition));
            engine.Add(
                new[]
                    {
                        new KeyValuePair<string, Type>("bounce", typeof(TransitionType_Bounce)),
                        new KeyValuePair<string, Type>("linear", typeof(TransitionType_Linear)),
                        new KeyValuePair<string, Type>("ease_inout", typeof(TransitionType_EaseInEaseOut)),
                        new KeyValuePair<string, Type>("throwcatch", typeof(TransitionType_ThrowAndCatch))
                    });
            engine.Add("iterator", new Func<object[], Iterator>(Functions.iterator));

            engine.Execute("function ActiveXObject(id) {return createObj(id);}");
        }

        #endregion
    }
}